using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using System;

namespace Assignment2_Console
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;

        private Texture2D ball;
        private Texture2D padLeft;
        private Texture2D padRight;
        private Texture2D pauseScreen;

        //Ball's velocity
        private Vector2 velocity;

        //Ball's properties
        private Vector2 ballPos;
        private Vector2 ballDiameter;
        private Vector2 ballPosZero;

        //Pads' property
        private Vector2 padPos1;
        private Vector2 padPos2;

        private Vector2 screenDimension;

        private SpriteFont font;
        private SpriteFont consoleFont;

        private float teamALength;
        private float teamBLength;

        private string pauseString;
        private Vector2 pausePosition;

        private int scoreA;
        private int scoreB;

        private bool isPause;
        private bool isConsoleActivated;

        private Color backgroundColor;

        //commandline 
        private string cmdString = ">";
        private string[] cmdWithArguments;
        private char[] separator = { ' ' };
        private string outputString = ">Welcome to console. What can I help you with?\n>Type 'help' for list of commands\n";
        private Commands cmd;
        private Vector2 cmdStringHeight;
        private int numberOfLine = 2;
        private int numberOfColumn = 1;
        private int numberOfLineTemporary = 1;
        private AnimatedSprite animatedSprite;

        //input wrapper class
        private InputProcessor inputListener;

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";

            inputListener = new InputProcessor(this);
            Components.Add(inputListener);
        }

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            // TODO: Add your initialization logic here
            isPause = true;
            isConsoleActivated = false;



            velocity = new Vector2(5);

            scoreA = 0;
            scoreB = 0;


#if WINDOWS
            pauseString = "Game paused. Press Enter to resume";
#elif XBOX
            pauseString="Game paused. Press Start to resume";
#endif


            backgroundColor = Color.CornflowerBlue;

            base.Initialize();

        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);

            // TODO: use this.Content to load your game content here
            consoleFont = Content.Load<SpriteFont>("Console");
            cmdStringHeight = new Vector2(0, consoleFont.MeasureString("bdfghjklpqtyA").Y - 2);

            ball = Content.Load<Texture2D>("img/ball");
            padLeft = Content.Load<Texture2D>("img/left");
            padRight = Content.Load<Texture2D>("img/right");

            screenDimension = new Vector2(graphics.GraphicsDevice.Viewport.Width, graphics.GraphicsDevice.Viewport.Height);

            ballDiameter = new Vector2(ball.Width, ball.Height);

            ballPosZero = screenDimension / 2 - ballDiameter / 2;
            ballPos = ballPosZero;

            padPos1 = new Vector2(0, screenDimension.Y / 2 - 50);
            padPos2 = new Vector2(screenDimension.X - 20, screenDimension.Y / 2 - 50);

            font = Content.Load<SpriteFont>("font");
            teamALength = font.MeasureString("Team A").X / 2;
            teamBLength = font.MeasureString("Team B").X / 2;

            pausePosition = screenDimension / 2 - font.MeasureString(pauseString) / 2;

            CreateRectangle((int)screenDimension.X, (int)screenDimension.Y, out pauseScreen, Color.White);

            Texture2D cursor = Content.Load<Texture2D>("img/cursor");
            animatedSprite = new AnimatedSprite(cursor, 1, 20);
        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            // Allows the game to exit
            if (inputListener.isButtonPressed(Buttons.Back, PlayerIndex.One))
                this.Exit();

            if (inputListener.isKeyPressed(Keys.Escape))
                this.Exit();


            // TODO: Add your update logic here


            if (!isConsoleActivated)
            {
                ToggleListener();
                if (!isPause)
                {
#if !XBOX
                    if (inputListener.gamePadConnected[0])
                    {
                        if (inputListener.gamePadConnected[1])
                        {
                            HandlleGamepad(inputListener.getLeftThumbstickValue(PlayerIndex.One).Y, ref padPos1.Y);
                            HandlleGamepad(inputListener.getLeftThumbstickValue(PlayerIndex.Two).Y, ref padPos2.Y);
                        }
                        else
                        {
                            HandlleGamepad(inputListener.getLeftThumbstickValue(PlayerIndex.One).Y, ref padPos1.Y);
                            HanndleKeyboard(ref padPos2.Y, true);
                        }
                    }
                    else
                    {
                        HanndleKeyboard(ref padPos1.Y, false);
                        HanndleKeyboard(ref padPos2.Y, true);
                    }
#else
            if (inputListener.gamePadConnected[1])
            {
                HandlleGamepad(inputListener.getLeftThumbstickValue(PlayerIndex.One).Y, ref padPos1.Y);
                HandlleGamepad(inputListener.getLeftThumbstickValue(PlayerIndex.Two).Y, ref padPos2.Y);
            }
            else
            {
                HandlleGamepad(inputListener.getLeftThumbstickValue(PlayerIndex.One).Y, ref padPos1.Y);
                HandlleGamepad(inputListener.getRightThumbstickValue(PlayerIndex.One).Y, ref padPos2.Y);
            }
#endif

                    UpdateBall();
                }
            }
            else
            {
                isPause = false;
                animatedSprite.Update();
                CommandListener();
            }
            base.Update(gameTime);
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(backgroundColor);

            // TODO: Add your drawing code here
            spriteBatch.Begin();
            if (!isConsoleActivated)
            {
                spriteBatch.Draw(padLeft, padPos1, Color.White);
                spriteBatch.Draw(padRight, padPos2, Color.White);
                spriteBatch.Draw(ball, ballPos, Color.White);
                spriteBatch.DrawString(font, "Team A\n   " + scoreA, new Vector2(screenDimension.X / 4 - teamALength, 10), Color.Cornsilk);
                spriteBatch.DrawString(font, "Team B\n   " + scoreB, new Vector2(screenDimension.X * 3 / 4 - teamBLength, 10), Color.Cornsilk);

                if (isPause)
                {
                    spriteBatch.Draw(pauseScreen, Vector2.Zero, Color.White * 0.5f);
                    spriteBatch.DrawString(font, pauseString, pausePosition, Color.Black);
                }
            }
            else
            {
                spriteBatch.Draw(pauseScreen, Vector2.Zero, Color.White);
                animatedSprite.Draw(spriteBatch, new Vector2(9 * numberOfColumn, (cmdStringHeight * (numberOfLine + numberOfLineTemporary - 0.2f)).Y));
                spriteBatch.DrawString(consoleFont, outputString, Vector2.Zero, Color.Black);
                spriteBatch.DrawString(consoleFont, cmdString, cmdStringHeight * numberOfLine, Color.Black);
            }
            spriteBatch.End();

            base.Draw(gameTime);
        }

        private void UpdateBall()
        {

            if (ballPos.X < 20)
            {
                if (ballPos.Y < padPos1.Y + 100 && ballPos.Y > padPos1.Y)
                {
                    ballPos.X = 20;
                }
                else
                {
                    scoreB++;
                    ballPos = ballPosZero;
                }
                velocity.X *= -1;
            }
            if (ballPos.X > screenDimension.X - ballDiameter.X - 20)
            {
                if (ballPos.Y < padPos2.Y + 100 && ballPos.Y > padPos2.Y)
                {
                    ballPos.X = screenDimension.X - ballDiameter.X - 20;
                }
                else
                {
                    scoreA++;
                    ballPos = ballPosZero;
                }
                velocity.X *= -1;
            }


            if (ballPos.Y < 0 || ballPos.Y > screenDimension.Y - ballDiameter.Y)
                velocity.Y *= -1;
            ballPos += velocity;

        }

        private void HandlleGamepad(float stickY, ref float tmpY)
        {
            if (tmpY >= 0 && tmpY <= screenDimension.Y - 100)
            {
                tmpY -= stickY * 5;
            }
            tmpY = (tmpY < 0) ? 0 : tmpY;
            tmpY = (tmpY > screenDimension.Y - 100) ? screenDimension.Y - 100 : tmpY;
        }
        private void HanndleKeyboard(ref float tmpY, bool isPlayer2)
        {
            if (isPlayer2)
            {

                if (tmpY > 0 && inputListener.isKeyDown(Keys.Up))
                {
                    tmpY -= 5;
                }
                tmpY = (tmpY < 0) ? 0 : tmpY;
                if (tmpY < screenDimension.Y - 100 && inputListener.isKeyDown(Keys.Down))
                {
                    tmpY += 5;
                }
                tmpY = (tmpY > screenDimension.Y - 100) ? screenDimension.Y - 100 : tmpY;
            }
            else
            {
                if (tmpY > 0 && inputListener.isKeyDown(Keys.W))
                {
                    tmpY -= 5;
                }
                tmpY = (tmpY < 0) ? 0 : tmpY;
                if (tmpY < screenDimension.Y - 100 && inputListener.isKeyDown(Keys.S))
                {
                    tmpY += 5;
                }
                tmpY = (tmpY > screenDimension.Y - 100) ? screenDimension.Y - 100 : tmpY;
            }
        }

        public void ToggleListener()
        {
            //toggle pause
            if (inputListener.isKeyPressed(Keys.Enter) || inputListener.isButtonPressed(Buttons.Start, PlayerIndex.One) || inputListener.isButtonPressed(Buttons.Start, PlayerIndex.Two))
            {
                isPause = !isPause;
            }

            //togle command
            if (inputListener.isKeyPressed(Keys.C))
            {
                outputString = ">Welcome to console. What can I help you with?\n>Type 'help' for list of commands\n";
                isConsoleActivated = !isConsoleActivated;
            }
        }
        #region Command
        public void CommandListener()
        {
            ProcessString();
            foreach (Keys presskey in Keyboard.GetState().GetPressedKeys())
            {
                if (inputListener.isKeyPressed(presskey))
                {
                    switch (presskey)
                    {
                        case Keys.Space:
                            cmdString += " ";
                            numberOfColumn++;
                            break;
                        case Keys.Back:
                            if (cmdString.Length > 1)
                            {
                                cmdString = cmdString.Remove(cmdString.Length - 1);
                                numberOfColumn--;
                            }
                            break;
                        case Keys.Enter:
                            numberOfColumn = 1;
                            cmdWithArguments = cmdString.Split(separator, StringSplitOptions.RemoveEmptyEntries);
                            try
                            {
                                cmd = (Commands)Enum.Parse(typeof(Commands), cmdWithArguments[0].TrimStart('>'));
                            }
                            catch (ArgumentException)
                            {
                                cmd = Commands.error;
                            }
                            if (cmdWithArguments.Length == 2 && cmdWithArguments[0].Equals(">bgcolor"))
                            {
                                switch (cmdWithArguments[1][0])
                                {
                                    case 'r':
                                        backgroundColor = Color.Red;
                                        outputString += cmdString + ":Background changed to RED\n";
                                        break;
                                    case 'b':
                                        backgroundColor = Color.Blue;
                                        outputString += cmdString + ":Background changed to BLUE\n";
                                        break;
                                    case 'g':
                                        backgroundColor = Color.Green;
                                        outputString += cmdString + ":Background changed to GREEN\n";
                                        break;
                                    case 'd':
                                        backgroundColor = Color.CornflowerBlue;
                                        outputString += cmdString + ":Background changed to DEFAULT\n";
                                        break;
                                    default:
                                        outputString += cmdString + ":invalid argument\n";
                                        break;
                                }
                                numberOfLine++;
                            }
                            else
                                switch (cmd)
                                {
                                    case Commands.exit:
                                        isConsoleActivated = false;
                                        numberOfLine = 2;
                                        isPause = true;
                                        break;
                                    case Commands.cls:
                                        outputString = "";
                                        numberOfLine = 0;
                                        break;
                                    case Commands.help:
                                        outputString += ">List of commands:\ncls:clear console screen\nexit: exit console\nbgcolor [value]: change background color value\n [value]=r|g|b|d\n  r for red\n  g for green\n  b for blue\n  d for default\nquit:exit game\n";
                                        numberOfLine += 10;
                                        break;
                                    case Commands.quit:
                                        this.Exit();
                                        break;
                                    case Commands.error:
                                        outputString += cmdString + ":invalid command\n";
                                        numberOfLine += numberOfLineTemporary;
                                        break;
                                }
                            numberOfLineTemporary = 1;
                            cmdString = ">";
                            break;
                        default:
                            if (presskey >= Keys.A && presskey <= Keys.Z)
                            {
                                cmdString += Enum.GetName(typeof(Keys), presskey);
                                numberOfColumn++;
                            }
                            break;
                    }
                }
            } cmdString = cmdString.ToLower();
        }

        public void ProcessString()
        {
            if (numberOfLine + numberOfLineTemporary - 1 > 24)
            {
                for (int i = 24; i < numberOfLine + numberOfLineTemporary - 1; i++)
                    outputString = outputString.Substring(outputString.IndexOf("\n") + 1);
                numberOfLine = 24 - (numberOfLineTemporary - 1);
            }
            if (numberOfColumn >= 80)
            {
                cmdString += '\n';
                numberOfLineTemporary++;
                numberOfColumn = 0;
            }
        }
        #endregion

        public void CreateRectangle(int width, int height, out Texture2D rectangleTexture, Color color)
        {
            // create the rectangle texture, ,but it will have no color! lets fix that
            rectangleTexture = new Texture2D(GraphicsDevice, width, height, false, SurfaceFormat.Color);

            // set the color to the amount of pixels
            Color[] colors = new Color[width * height];

            // loop through all the colors setting them to whatever values we want
            for (int i = 0; i < colors.Length; i++)
            {
                colors[i] = color;
            }

            // set the color data on the texture
            rectangleTexture.SetData(colors);

        }


    }
}
